package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"strings"
)

/**
php 版本:
function isInRange($ip,$segment){

	$binaryIp = ip2long($ip);
	$segemnt_arr = explode("/",$segment);
	$type = $segemnt_arr[1];
	$mask = 0xFFFFFFFF << (32 - intval($type));
	$segmentIp = $segemnt_arr[0];
	$binarySegmentIp = ip2long($segmentIp);
	return ($binaryIp & $mask) == ($binarySegmentIp & $mask);
}
*/
func ip2long(ip net.IP) uint32 {
	var long uint32
	binary.Read(bytes.NewBuffer(ip.To4()), binary.BigEndian, &long)
	return long
}
func long2ip(ip uint32) net.IP {
	a := byte((ip >> 24) & 0xFF)
	b := byte((ip >> 16) & 0xFF)
	c := byte((ip >> 8) & 0xFF)
	d := byte(ip & 0xFF)
	return net.IPv4(a, b, c, d)
}

// ip1  ipv4
// segment 可能为网段,可能为 ip
func verify(ip1, segment string) bool {
	ip := net.ParseIP(ip1)
	// 仅仅判断 ipv4
	if ip.To4() == nil {
		fmt.Printf("%v is not an IPv4 address\n", ip)
		return false
	}
	// 精准 ip 判断
	if !strings.Contains(segment, "/") {
		if bytes.Compare(ip, net.ParseIP(segment)) == 0 {
			return true
		}
		return false
	}
	// 网关判断
	binaryIp := ip2long(ip)
	segemnt_arr := strings.Split(segment, "/") // example: 216.14.49.184/24 ===> 216.14.49.184  24
	t := segemnt_arr[1]
	i, _ := strconv.Atoi(t)
	mask := 0xFFFFFFFF << (32 - uint64(i))
	segmentIp := segemnt_arr[0]
	binarySegmentIp := ip2long(net.ParseIP(segmentIp))
	if binaryIp&uint32(mask) == binarySegmentIp&uint32(mask) {
		return true
	}
	return false
}
func main() {
	ips := map[string]string{}
	ips["217.171.224.66"] = "217.171.224.1/16"
	ips["217.171.224.1"] = "217.171.224.1"
	ips["127.0.0.1"] = "127.0.0.1"
	ips["::1"] = "::1"
	for k, v := range ips {
		if verify(k, v) {
			fmt.Println("==> pass")
		} else {
			fmt.Println("==> fail")
		}

	}

}
